package myJs.user.data

import myJs.Implicits.{JArJDS, _}
import myJs.Utils
import myJs.Utils._
import scala.scalajs.js.Dynamic.{global => g}
import myJs.components.TagComponent
import myJs.myPkg._
import myJs.myPkg.bootstrap.Bootstrap.default._
import myJs.myPkg.jquery._
import myJs.tool.Tool
import myJs.tool.Tool._
import myJs.user.data.VarInfoTab.{tabId, updateFormId}
import myJs.user.soft.fileSelect.FileModal
import org.scalajs.dom.ext.Ajax
import org.scalajs.dom.{FormData, XMLHttpRequest, _}
import scalatags.Text.all._

import scala.collection.immutable.SeqMap
import scala.scalajs.js
import scala.scalajs.js.JSConverters._
import scala.scalajs.js.{Date, Dictionary, JSON, UndefOr}
import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel}
import shared.Pojo._
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._

/**
 * Created by yz on 11/8/2020
 */
@JSExportTopLevel("MetDataTab")
object MetDataTab {

  val tabId = "metData"
  val tableId = "table"
  val updateFormId = "updateForm"
  val subFormId = "subForm"
  val updateModalId = "updateModal"
  val viewModalId = "viewModal"
  val subModalId = "subModal"
  val dataManageId = "dataManage"

  def init = {
    initTable
    bootStrapValidator
    updateFormBootStrapValidator
    ExtraGroupModal.init
    ExtraVarInfoModal.init
    NewGroupModal.init
    registerEvent
    Tool.popover
    SubModal.init
    FileModal.init

    //    matrixFileIntroShow

  }

  def tableF = {
    val url = g.jsRoutes.controllers.MatrixFileDataController.getAll().url.toString
    Ajax.get(url = s"${url.noCache}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      xhr.responseText.toJArJDJAn.toMyJDS
    }
  }

  def registerEvent = {
    $(document.body).on("click", s"#${tabId} .myUpdateShow", "", (y: Element) => {
      val dict = $(y).dataRow.toJDS
      updateShow(dict)
    })
    $(document.body).on("click", s"#${tabId} .myUpdate", "", (y: Element) => {
      update
    })
    $(document.body).on("click", s"#${tabId} .myViewShow", "", (y: Element) => {
      val id = $(y).dataId
      viewShow(id)
    })
    $(document.body).on("click", s"#${tabId} .myDelete", "", (y: Element) => {
      val id = $(y).dataId
      delete(id)
    })
    $(document.body).on("click", s"#${tabId} .myAdd", "", (y: Element) => {
      add
    })
    $(s"#${tabId} #${updateModalId}").on("shown.bs.modal", () => {
      $(s"#${tabId} #${updateFormId}").bv.revalidateField("fileName")
    })
    $(s"#${tabId} #${updateModalId}").on("hide.bs.modal", () => {
      $(s"#${tabId} #${updateFormId}").bv.resetForm(true)
    })
    $(s"#${tabId} #${subModalId}").on("shown.bs.modal", () => {
      $(s"#${tabId} #${subFormId}").bv.revalidateField("fileName")
    })
    $(s"#${tabId} #${subModalId}").on("hide.bs.modal", () => {
      $(s"#${tabId} #${subFormId}").bv.resetForm(true)
    })
    $(document.body).on("click", s"#${tabId} .idsButton", "", (y: Element) => {
      deletes(s"${dataManageId} #${tabId}")
    })
    $(document.body).on("click", s"#${tabId} .myGroupShow", "", (y: Element) => {
      val id = $(y).dataId
      NewGroupModal.groupShow(id)
    })
    $(document.body).on("click", s"#${tabId} .mySubShow", "", (y: Element) => {
      val id = $(y).dataId
      subShow(id)
    })
    $(document.body).on("click", ".myFileSelect", "", (y: Element) => {
      val name = $(y).dataInputName
      val lis = $(y).dataValue.split(";")
      $(".myFileSelect").removeClass("myCurrent")
      $(y).addClass("myCurrent")
      FileModal.fileSelectShow(lis)
    })
    $(document.body).on("click", s".myFileSelectButton", "", (y: Element) => {
      val folder = $(y).dataFolder
      val fileName = $(y).dataFileName
      val modalJq = $(".myFileSelect.myCurrent").parents(".modal")
      val id = modalJq.attr("id").get
      id match {
        case "extraGroupModal" => ExtraGroupModal.fileSelect(folder, fileName)
        case "extraVarInfoModal" => ExtraVarInfoModal.fileSelect(folder, fileName)
      }
    })
    $(document.body).on("click", s"#${tabId} .myMatrixFileIntroShow", "", (y: Element) => {
      matrixFileIntroShow
    })
  }

  def matrixFileIntroShow = {
    val options = LayerOptions.`type`(1).title(s"<h4>${messages("MatrixFileFormatDescription")}</h4>").area(js.Array("900px", "600px")).
      skin("layui-layer-demo").closeBtn(1).anim(2).shadeClose(true).maxmin(true).
      content($(s"#matrixFileIntro"))
    layer.open(options)
  }

  def deletes(parentId: String) = {
    val ids = Tool.getIdsValue(parentId)
    val options = SwalOptions(
      title = "",
      text = messages("deleteConfirms"),
      `type` = "warning",
      showCancelButton = true,
      showConfirmButton = true,
      confirmButtonClass = Tool.confirmButtonClass,
      confirmButtonText = messages("confirm"),
      closeOnConfirm = false,
      cancelButtonText = messages("cancel"),
      cancelButtonClass = Tool.cancelButtonClass
    )
    Swal.swal(options, () => {
      existSub(ids).map { exist =>
        if (exist) {
          Swal.close()
          val confirmOptions = LayerOptions.btn(js.Array(messages("yes"), messages("no")))
          layer.confirm(messages("TheSubDataExistsInSelectedData"), confirmOptions, (confirmIndex: Int) => {
            layer.close(confirmIndex)
            execDeleteWithSub(ids)
          }, () => (
            execDelete(ids)
            ))
        } else {
          execDelete(ids)
        }
      }
    })
  }

  def execDelete(ids: List[String], parentId: String = "") = {
    val data = js.Dictionary(
      "ids" -> ids.toJSArray
    )
    val url = g.jsRoutes.controllers.MatrixFileDataController.deletes().url.toString
    Ajax.post(url = s"${
      url
    }", data = JSON.stringify(data),
      headers = Map("Content-Type" -> "application/json"), responseType = "blob").map {
      xhr =>
        refreshTable {
          () =>
            val options = SwalOptions(
              `type` = "success",
              title = messages("success"),
              text = s"${messages("deleteSuccessfully")}！"
            )
            Swal.swal(options)
            Tool.getIds(parentId)
        }
    }
  }

  def delete(id: String) = {
    val options = SwalOptions(
      title = "",
      text = messages("deleteConfirm"),
      `type` = "warning",
      showCancelButton = true,
      showConfirmButton = true,
      confirmButtonClass = Tool.confirmButtonClass,
      confirmButtonText = messages("confirm"),
      closeOnConfirm = false,
      cancelButtonText = messages("cancel"),
      showLoaderOnConfirm = true,
      cancelButtonClass = Tool.cancelButtonClass
    )
    Swal.swal(options, () => {
      existSub(List(id)).map { exist =>
        if (exist) {
          Swal.close()
          val confirmOptions = LayerOptions.btn(js.Array(messages("yes"), messages("no")))
          layer.confirm(messages("TheSubDataExistsInSelectedData"), confirmOptions, (confirmIndex: Int) => {
            layer.close(confirmIndex)
            execDeleteWithSub(List(id))
          }, () => (
            execDelete(List(id))
            ))
        } else {
          execDelete(List(id))
        }
      }
    })
  }

  def execDeleteWithSub(ids: List[String]) = {
    val url = g.jsRoutes.controllers.MatrixFileDataController.deleteWithSub().url.toString
    val data = js.Dictionary(
      "ids" -> ids.toJSArray
    )
    Ajax.delete(url = s"${url}", data = JSON.stringify(data), headers = Map("Content-Type" -> "application/json")).map { xhr =>
      refreshTable { () =>
        val options = SwalOptions(
          `type` = "success",
          title = messages("success"),
          text = messages("deleteSuccessfully")
        )
        Swal.swal(options)
      }
    }
  }

  def existSub(ids: List[String]) = {
    val url = g.jsRoutes.controllers.MatrixFileDataController.existSub().url.toString
    val data = js.Dictionary(
      "ids" -> ids.toJSArray
    )
    Ajax.post(url = s"${url}", data = JSON.stringify(data), headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val rs = xhr.responseText.toJDJAn
      rs("exist").toB
    }
  }

  def getNewFileName(id: String) = {
    val url = g.jsRoutes.controllers.MatrixFileDataController.getNewFileName().url.toString
    Ajax.get(url = s"${url.noCache}&id=${id}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val rs = xhr.responseText.toJDJAn.toMyJDS
      rs("fileName").fileNamePrefix
    }
  }

  def multiSelectInit = {
    val rowOptions = MultiSelectOptions.keepRenderingSort(false).sort(false)
    $("#oldRows").multiselect(rowOptions)
    val colOptions = MultiSelectOptions.keepRenderingSort(false)
    $("#oldCols").multiselect(colOptions)
  }

  def subShow(id: String) = {
    val modalId = subModalId
    val url = g.jsRoutes.controllers.MatrixFileDataController.getHeaders().url.toString
    Ajax.get(url = s"${url.noCache}&id=${id}", headers = Map("Content-Type" -> "application/json")).flatMap { xhr =>
      getNewFileName(id).map { newFileName =>
        val rs = xhr.responseText.toJDJAn
        val headers = rs("headers").toJArS
        val html = headers.zipWithIndex.map { case (v, i) =>
          option(value := s"${v}", dataId := s"${i}", title := s"${v}", v)
        }.mkString("&nbsp;")
        $("#oldCols").html(html)
        val rows = rs("rows").toJArS
        val rowHtml = rows.zipWithIndex.map { case (v, i) =>
          option(value := s"${v}", dataId := s"${i}", title := s"${v}", v)
        }.mkString("&nbsp;")
        $("#oldRows").html(rowHtml)
        multiSelectInit
        $(s"#${modalId} #fileName").text(rs("fileName").toString)
        $(s"#${modalId}").findInputByName("fileName").`val`(newFileName)
        $(s"#${modalId}").findInputByName("kind").`val`(MetDataKind.name)
        $(s"#${modalId}").findInputByName("id").`val`(id)
        $(s"#${modalId}").findInputByName("rows[]").children().remove()
        $(s"#${modalId}").findInputByName("cols[]").children().remove()
        $(s"#${subModalId} #left #totalNum").text(s"${rows.size}")
        $(s"#${subModalId} #left #remainNum").text(s"${rows.size}")
        $(s"#${subModalId} #right #totalNum").text(s"${headers.size}")
        $(s"#${subModalId} #right #remainNum").text(s"${headers.size}")
        $(s"#${subModalId} #left #num").text(s"0")
        $(s"#${subModalId} #right #num").text(s"0")
        $(s"#${subModalId}").modal("show")
      }
    }
  }

  def add = {
    val bv = $(s"#${tabId} #form").bv
    bv.validate()
    val valid = bv.isValid()
    if (valid) {
      val formData = new FormData(document.getElementById("form").toHTMLFormElement)
      val inputKind = $(s"#${tabId} #inputKind li.active").text().trim
      formData.append("inputKind", inputKind)
      val element = div(id := "content",
        span(id := "info", messages("running"),
          span(id := "progress", "。。。")), " ",
        img(src := "/assets/images/running2.gif", cls := "runningImage", width := 30, height := 20)
      ).render
      val layerOptions = LayerOptions.title(Tool.zhInfo).closeBtn(0).skin("layui-layer-molv").btn(js.Array())
      val index = layer.alert(element, layerOptions)
      val url = g.jsRoutes.controllers.MatrixFileDataController.add().url.toString
      val xhr = new XMLHttpRequest
      xhr.open("post", url)
      xhr.upload.onprogress = Utils.progressHandlingFunction
      xhr.onreadystatechange = (e) => {
        if (xhr.readyState == XMLHttpRequest.DONE) {
          val data = xhr.response
          val rs = JSON.parse(data.toString).toJDJAn
          layer.close(index)
          val valid = rs("valid").toB
          $(s"#${tabId} #addModal").modal("hide")
          if (valid) {
            refreshTable { () =>
              val options = SwalOptions(
                `type` = "success",
                title = messages("success"),
                text = s"${messages("uploadSuccessfully")}!"
              )
              Swal.swal(options)
            }
          } else {
            g.swal("Error", rs.myGet("message"), "error")
          }
        }
      }
      xhr.send(formData)
    }
  }

  def viewShow(id: String) = {
    val url = g.jsRoutes.controllers.MatrixFileDataController.getFileDataById().url.toString
    Ajax.get(url = s"${url.noCache}&id=${id}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val dict = xhr.responseText.toJDJAn.toMyJDS
      dict.foreach { case (k, v) =>
        val trueV = k match {
          case "size" => span(Tool.getFileSize(v))
          case "fileName" => span(v)
          case "tags" => span(Tool.tagMap(v))
          case _ => span(v)
        }
        $(s"#fileInfo #${k}").html(trueV.render)
      }
      $(s"#${viewModalId}").modal("show")
    }

  }

  def update = {
    val formId = updateFormId
    val modalId = updateModalId
    val bv = $(s"#${tabId} #${formId}").bv
    bv.validate()
    val valid = bv.isValid()
    if (valid) {
      val data = $(s"#${tabId} #${formId}").serialize()
      val index = layer.alert(Tool.myElement, Tool.layerOptions)
      val url = g.jsRoutes.controllers.MatrixFileDataController.update().url.toString
      Ajax.post(url = s"${url}", data = data, headers = Map("Content-Type" -> "application/x-www-form-urlencoded")).map { xhr =>
        refreshTable { () =>
          layer.close(index)
          $(s"#${tabId} #${modalId}").modal("hide")
          bv.resetForm(true)
          val options = SwalOptions(
            `type` = "success",
            title = messages("success"),
            text = s"${messages("modifySuccessfully")}!"
          )
          Swal.swal(options)
        }
      }
    }
  }

  def refreshTable(f: () => js.Any = () => ()) = {
    val tabId = s"${dataManageId} #${Tool.getCurTabId}"
    val queryMap = Tool.getQueryMap(tabId)
    println(queryMap)
    tableF.map { data =>
      val sortData = DataManage.sortExec(data, queryMap)

      def loopFilter(sortData: JArJDS) = {

        val filterColumns = queryMap.removedAll(List("sort", "order")).filter(_._2.map(_.trim).nonEmpty).keyList

        def loop(acc: JArJDS, elems: List[String]): JArJDS = {
          elems match {
            case Nil => acc
            case y :: ys =>
              val searchV = queryMap(y)
              val trueSearchV = searchV.head
              val fieldName = y.split("\\.")(0)
              val name = y.split("\\.")(1)
              val curFilterData = {
                fieldName match {
                  case "size" => val (minSize, maxSize) = trueSearchV match {
                    case "0KB-100KB" => (0, 100 * 1024)
                    case "100KB-1MB" => (100 * 1024, 1 * 1024 * 1024)
                    case "1MB-10MB" => (1 * 1024 * 1024, 10 * 1024 * 1024)
                  }
                    acc.filter { row =>
                      val size = row(fieldName).toInt
                      size <= maxSize && size >= minSize
                    }
                  case "tags" => acc.filter { row =>
                    Tool.tagMap(row(fieldName)).contains(trueSearchV)
                  }
                  case x if DataManage.timeFieldNames.contains(x) => acc.filter { row =>
                    val dbDate = row(fieldName)
                    if (name == "startTime") {
                      Tool.timeBefore(trueSearchV, dbDate)
                    } else {
                      Tool.timeAfter(trueSearchV, dbDate)
                    }
                  }
                  case _ => acc.filter { row =>
                    row(fieldName).containsIgnoreCase(trueSearchV)
                  }
                }
              }
              loop(curFilterData, ys)
          }
        }

        loop(sortData, filterColumns)
      }

      val filterData = loopFilter(sortData)
      $(s"#${tableId}").bootstrapTable("load", filterData)
      f()
    }
  }

  def tbFmt(columnName: String): js.Function = (v: js.Any, row: js.Dictionary[js.Any]) => columnName match {
    case "fileName" =>
      val subStr = a(title := messages("Submatrix"), cursor.pointer, dataId := s"${row("id")}", `class` := "mySubShow",
        target := "_blank",
        img(src := "/assets/images/data_filter.png", cls := "runningImage", width := 20)
      )
      val url = g.jsRoutes.controllers.MatrixFileDataController.detailBefore().url.toString
      val viewStr = a(href := s"${url}?id=${row("id")}", target := "_blank", s"${v.toString}")
      List(subStr, viewStr).mkString("&nbsp;")
    case "size" => Tool.getFileSize(v)
    case "tags" => Tool.tagMap.getOrElse(v.toString, v.toString)
    case _ => v
  }

  lazy val operateColumn = js.Array(
    ColumnOptions.field("operate").title(messages("operations")).formatter(operateFmt).filterControl("clear")
  )

  def updateShow(dict: js.Dictionary[String]) = {
    val fillDict = dict.map { case (k, v) =>
      val trueV = k match {
        case "fileName" => dict("fileName").fileNamePrefix
        case _ => v
      }
      (k, trueV)
    }.toJSDictionary
    Tool.fillByNames(fillDict, $(s"#${tabId} #${updateFormId}"))
    $(s"#${tabId} #${updateFormId} :input[name='originalFileName']").`val`(dict("fileName").fileNamePrefix)
    val tags = dict("tags")
    TagComponent.Component().renderIntoDOM(document.querySelector(s"#${tabId} #${updateFormId} .tag")).
      setState(TagComponent.State(tags))
    $(s"#${tabId} #${updateModalId}").modal("show")
  }

  @JSExport("operateFmt")
  def operateFmt: js.Function = {
    (v: js.Any, tmpRow: js.Dictionary[js.Any]) =>
      val row = tmpRow.toMyJDS
      val deleteStr = a(
        title := messages("delete"), `class` := "myDelete",
        cursor.pointer,
        dataId := s"${row("id")}",
        target := "_blank",
        span(
          em(cls := "fa fa-close")
        )
      )
      val updateStr = a(title := messages("modify"), dataRow := s"${JSON.stringify(row)}", `class` := "myUpdateShow", cursor.pointer,
        target := "_blank", span(em(cls := "fa fa-edit"))
      )
      val viewStr = a(title := messages("view"), cursor.pointer, dataId := s"${row("id")}", `class` := "myViewShow",
        target := "_blank", span(em(cls := "fa fa-eye"))
      )

      val downloadUrl = g.jsRoutes.controllers.MatrixFileDataController.downloadFileById().url.toString
      val downloadStr = a(title := messages("download"), href := s"${downloadUrl}?id=${row("id")}", cursor.pointer,
        span(em(cls := "fa fa-download"))
      )

      val groupStr = a(title := messages("newGroup"), cursor.pointer, dataId := s"${row("id")}", `class` := "myGroupShow",
        target := "_blank", span(em(cls := "fa fa-group"))
      )
      val rs = List(viewStr, downloadStr, updateStr, groupStr, deleteStr)
      rs.mkString("&nbsp;")
  }

  def onPostBody: js.Function = () => {
    val tableJq = $(s"#${tabId} #${tableId}")
    val options = tableJq.bootstrapTable("getOptions").toJDJAn
    val columnsOp = options.get("columns")
    columnsOp.foreach { columns =>
      val finalColumns = columns.asInstanceOf[js.Array[js.Array[js.Any]]]
      val visible = finalColumns(0)(1).toJDJAn("visible").toB
      if (visible) {
        def onChange: js.Function = () => {
          tableJq.bootstrapTable("resetView")
        }

        val treeGridOptions = TreeGridOptions.treeColumn(1).onChange(onChange)
        tableJq.treegrid(treeGridOptions)
      }
    }
  }

  def initTable = {
    DataManage.initFilterContent(tabId)
    val map = SeqMap("fileName" -> messages("fileName"), "tags" -> messages("tag"),
      "comment" -> messages("comment"), "size" -> messages("size"),
      "uploadTime" -> messages("uploadModifyTime"))
    val columnNames = map.keyList
    val columns = js.Array(Utils.checkColumn) ++ columnNames.map { columnName =>
      val title = map.getOrElse(columnName, columnName)
      val fmt = tbFmt(columnName)
      val style = DataManage.expStyle(columnName)
      val basicOptions = ColumnOptions.field(columnName).title(title).sortable(true).formatter(fmt).cellStyle(style)
      columnName match {
        case x if List("size", "uploadTime", "fileName", "comment", "tags").contains(x) => basicOptions.
          sortable(false).operateable(true)
        case _ => basicOptions
      }
    }.concat(operateColumn)
    val options = TableOptions.columns(columns.toJSArray).searchOnButton(false).filterControl(false).idField("id").
      parentIdField("pId").treeShowField("fileName").treeEnable(true).rootParentId("0").onPostBody(onPostBody).
      locale(Tool.getBTLanguage)
    $(s"#${tabId} #${tableId}").bootstrapTable(options)
    Tool.dateInit
    Tool.bindEvt(s"dataManage #${tabId}")
  }

  def bootStrapValidator = {
    val maxNumber = Double.MaxValue
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "fileContent" ->{
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${messages("null")}"
              ),
            )
          )
        } ,
        "file" ->{
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${messages("null")}"
              ),
              "file" -> js.Dictionary(
                "message" -> messages("IncorrectFormat"),
                "extension" -> "txt,xlsx,csv",
              ),
            )
          )
        } ,
      )
    )
    $(s"#${tabId} #form").bootstrapValidator(dict)
  }

  def updateFormBootStrapValidator = {
    def dataF: js.Function = (validator: BootstrapValidator) => {
      js.Dictionary(
        "kind" -> validator.getFieldElements("kind").myVal,
        "originalFileName" -> validator.getFieldElements("originalFileName").myVal
      )
    }

    val maxNumber = Double.MaxValue
    val url = g.jsRoutes.controllers.MatrixFileDataController.fileNameNonExistCheck().url.toString
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "originalFileName" -> js.Dictionary(),
        "fileName" -> {
          val info = messages("fileName")
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${messages("null")}"
              ),
              "remote" -> js.Dictionary(
                "message" -> s"${messages("exist")}",
                "url" -> url,
                "delay" -> 1000,
                "type" -> "POST",
                "data" -> dataF
              ),
            )
          )
        },
      )
    )
    $(s"#${tabId} #${updateFormId}").bootstrapValidator(dict)
  }


}
